home *** CD-ROM | disk | FTP | other *** search
- /*
- * QLIPO -- NeXT compatible, portable LIPO.
- *
- * You may not remove this comment header, though you may add to it.
- * You may not remove attributions to the author or contributors, though
- * you may add to them.
- *
- * Darcy Brockbank <samurai@hasc.ca>
- * Hutchison Ave. Software
- */
- #define PROGRAM_NAME "qlipo"
- #define VERSION "Version 1.3."
- /*
- * Version 1.3
- *
- * - using stat() to verify identical files, rather than strcmp() (duh.)
- * Apr 3, 1995
- *
- * Version 1.2
- *
- * - allowed file-over-file overwrite specification
- * - caught signals for cleanup
- *
- * Version 1.0 <BETA>
- * Dec 22, 1994
- *
- * This program is loosely based on plipo, by Christian Scheider, which
- * served mainly as example code and inspiration that this could be done.
- * The intent of this program is to furnish an upgrade path for people
- * who have broken versions of 'lipo' as under NEXTSTEP 3.2 and previous
- * versions. QLIPO is bug-for-bug compatible with lipo... well, not exactly
- * bug-for-bug, but it does a good job of handling all the arguments you
- * can pass it, while giving some more useful messages than our old
- * buggy friend lipo.
- *
- * For the most part, this program is portable to any UNIX, though it works
- * only on MACH-O files. At the moment, I'm being very conservative by
- * keeping the byte alignments of the fat files at 8192 byte boundaries.
- * While this will result in a bit of wasted space, it makes sure that
- * all executables are happy. Future versions will allow for less
- * conservative alignments.
- *
- * This program is being provided with the hope it will be interesting,
- * and perhaps useful, but no guarantees are made as to it's functionality
- * or usefulness, nor do I or Hutchison Ave. Software warrant it in any
- * other way.
- *
- * Please keep me informed as to incompatabilities or problems, and I
- * will do my best to fix things. Lots of this code is gross... I'm
- * sorry about that, but I'm just trying to get it to work. Typical
- * C programmer's excuse. I did write this over the course of 24 hours
- * though, so please be gentle with me.
- *
- * TODO
- *
- * 1) support segalign
- * 2) support multiple -remove flags... I had no idea this could be
- * specified.
- *
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "cpu.h"
- #include <math.h>
- #include <errno.h>
- #include <sys/stat.h>
- #ifdef NeXT
- # include <libc.h>
- #else
- # include <unistd.h>
- #endif
- #include <signal.h>
-
- #define FAT_MAGIC 0xcafebabe
- #define NON_FAT_BIG_ENDIAN 0xfeedface
- #define NON_FAT_LITTLE_ENDIAN 0xcefaedfe
- #define STRDUP(a) (strcpy((char *)malloc(strlen((a))+1)),a)
-
- /* sizeof() will pad the struct */
- #define HEADER_BYTE_COUNT 8
- typedef struct fat_header
- {
- char magic[4]; /* FAT_MAGIC */
- char nfat_arch[4]; /* number of structs that follow */
- } fat_header_t;
-
- /* sizeof() will pad the struct */
- #define ARCH_BYTE_COUNT 20
- typedef struct fat_arch
- {
- char cputype[4]; /* cpu specifier (int) */
- char cpusubtype[4]; /* machine specifier (int) */
- char offset[4]; /* file offset to this object file */
- char size[4]; /* size of this object file */
- char align[4]; /* alignment as a power of 2 */
- } fat_arch_t;
-
- typedef struct non_fat_header
- {
- char magic[4]; /* FAT_MAGIC */
- char cputype[4]; /* cpu specifier (int) */
- } non_fat_header_t;
-
- /* types of files */
- #define MH_OBJECT 0x1
- #define MH_EXECUTE 0x2
- #define MH_FVMLIB 0x3
- #define MH_CORE 0x4
- #define MH_PRELOAD 0x5
- #define MH_DYLIB 0x6
- #define MH_DYLINKER 0x7
- #define MH_BUNDLE 0x8
-
- /* flags */
- #define MH_NOUNDEFS 0x1
- #define MH_INCRLINK 0x2
- #define MH_DYLDLINK 0x4
-
- #define LOADER_BYTE_COUNT 28
- typedef struct loader
- {
- char magic[4]; /* magic number */
- char cputype[4]; /* machine specifier (int) */
- char cpusubtype[4]; /* machine specifier (int) */
- char filetype[4]; /* the kind of file */
- char ncmds[4]; /* size of all the load commands */
- char sizeofcmds[4]; /* size of all the load commands */
- char flags[4]; /* flags */
- } loader_t;
-
- #define GET32(x) ((unsigned long)((((unsigned char *)(x))[0] << 24) | \
- (((unsigned char *)(x))[1] << 16) | \
- (((unsigned char *)(x))[2] << 8) | \
- (((unsigned char *)(x))[3])))
-
- #define GET32L(x) ((unsigned long)((((unsigned char *)(x))[3] << 24) | \
- (((unsigned char *)(x))[2] << 16) | \
- (((unsigned char *)(x))[1] << 8) | \
- (((unsigned char *)(x))[0])))
-
- #define SET32(a, x) ((((unsigned char *)(a))[0] = (unsigned char )(x >> 24)), \
- (((unsigned char *)(a))[1] = (unsigned char )(x >> 16)), \
- (((unsigned char *)(a))[2] = (unsigned char )(x >> 8)), \
- (((unsigned char *)(a))[3]) = (unsigned char )x)
-
- #define COPY32(d, s) (((char *)(d))[0] = ((char *)(s))[0]), \
- (((char *)(d))[1] = ((char *)(s))[1]), \
- (((char *)(d))[2] = ((char *)(s))[2]), \
- (((char *)(d))[3] = ((char *)(s))[3]);
-
-
- #define IS_FAT(hdr) (GET32(((hdr)->magic)) == FAT_MAGIC)
-
- #define IS_MAGIC(hdr) (GET32(((hdr)->magic)) == FAT_MAGIC || \
- GET32(((hdr)->magic))==NON_FAT_BIG_ENDIAN || \
- GET32(((hdr)->magic))==NON_FAT_LITTLE_ENDIAN)
-
- /* 2^13 == 8192 */
- #define DEFAULT_ALIGN 13
-
- /*
- * one of the operations we can perform...
- */
-
- typedef enum {
- _info=0,
- _detailed_info,
- _create,
- _thin,
- _replace,
- _remove,
- _extract,
- _arch, /* non unique */
- _output, /* non unique */
- _segalign /* non unique ... and ignored under this version */
- } operation_type_t;
-
- typedef struct _operation_t {
- operation_type_t op;
- char * argv[3];
- int argc;
- } operation_t;
-
- /*
- * The switches we're looking for, and their matching arguments.
- */
-
- typedef struct _arg_t {
- const char * name;
- int numargs;
- operation_type_t op;
- } arg_t;
-
- /*
- * Argument parsing aid .
- */
- const arg_t argument_set[] = {
- {"-info",0,_info},
- {"-detailed_info",0,_detailed_info},
- {"-arch",2,_arch},
- {"-output",1,_output},
- {"-o",1,_output},
- {"-create",0,_create},
- {"-thin",1,_thin},
- {"-replace",2,_replace},
- {"-remove",1,_remove},
- {"-extract",1,_extract},
- {"-segalign",2,_segalign},
- {0}
- };
-
- /*
- * Filename and association.
- */
- typedef struct _filelist_t {
- char * filename; /* our name */
- int cputype; /* our cpu */
- int cpusubtype; /* and subtype */
- off_t size; /* our size */
- long start; /* which fat arch we own in the list */
- int isfat; /* are we fat? */
- int skip; /* an arch to skip */
- int suck; /* the only arch to keep */
- int narch; /* the number of architectures */
- unsigned long mode; /* the permissions */
- } filelist_t;
-
- /*
- * A list of named input files. Null terminated, and counted for your
- * convenience.
- */
- filelist_t ** input_files=0;
- int input_file_count=0;
-
- /*
- * An output file.
- */
- filelist_t output_file={0,CPU_TYPE_ANY,0};
-
- /*
- * An overwrite file.
- */
- static char * overwrite_file = 0;
-
-
- /*
- * A buffer for everyone to play in.
- */
- #define BUFFER_SIZE 8192
- static char buffer[BUFFER_SIZE];
-
- #define DEBUG
- #ifdef DEBUG
- static int ArgC=0;
- static char ** ArgV=0;
- static void
- quit(int flag)
- {
- if (flag){
- int i;
- fprintf(stderr,"ArgV: qlipo ");
- for(i=1;i<ArgC;i++){
- fprintf(stderr,"%s ",ArgV[i]);
- }
- fprintf(stderr,"\n");
- }
- if (overwrite_file) {
- unlink(overwrite_file);
- }
- exit(flag);
- }
- #else
- # define quit(flag) exit(flag)
- #endif
-
-
-
- static void
- usage(void)
- {
- fprintf(stderr,
- "qlipo: NeXT compatible lipo, by Darcy Brockbank <samurai@hasc.ca>\n"
- " based on 'plipo' by Christian Schneider. " VERSION "\n"
- "Note: ONLY one of -create, -thin <arch_type>, -extract <arch_type>,\n"
- " -remove <arch_type>, -replace <arch_type> <file_name>, -info \n"
- " or -detailed_info must be specified.\n"
- "Usage: qlipo [input_file] ... [-arch <arch_type> input_file] ...\n"
- " [-info] [-detailed_info] [-output output_file] [-create]\n"
- " [-thin <arch_type>] [-extract <arch_type] ... [-remove <arch_type>]\n"
- " ... [-replace <arch_type> <file_name>] ...\n");
- quit(1);
- }
-
- static void
- stat_file(int which)
- {
- const char * filename = input_files[which]->filename;
- struct stat sb;
- stat(filename,&sb);
- input_files[which]->size = sb.st_size;
- input_files[which]->mode = sb.st_mode;
- }
-
- /*
- * Returns a structure representing the switch in the arglist, and what
- * it is. If it's nothing (ie. a filename) then returns NULL.
- */
- const arg_t *
- switch_at(char *argv[],int i)
- {
- const arg_t *op;
- for(op=argument_set;op->name;op++){
- if (strcmp(op->name,argv[i])==0){
- return op;
- }
- }
- return 0;
- }
-
- /*
- * Could expand this to check for syntax, but not right now
- */
-
- int
- switch_argc(const arg_t *op)
- {
- if (!op) return 0;
- return op->numargs;
- }
-
- static void
- grok_fileinfo(void)
- {
- FILE * file;
- long i;
- loader_t header;
- if (!input_file_count){
- fprintf(stderr,"%s: No input files specified.\n", PROGRAM_NAME);
- usage();
- }
- for(i=0;i<input_file_count;i++){
- file = fopen(input_files[i]->filename,"r");
- if (!file){
- fprintf(stderr,"%s: Can't open input file \"%s\", (%s)\n",
- PROGRAM_NAME, input_files[i]->filename,strerror(errno));
- quit(1);
- }
- if (fread(&header,1,LOADER_BYTE_COUNT,file)==LOADER_BYTE_COUNT){
- switch(GET32(header.magic)){
- case NON_FAT_BIG_ENDIAN:
- input_files[i]->cputype=GET32(header.cputype);
- input_files[i]->cpusubtype=GET32(header.cpusubtype);
- input_files[i]->narch = 1;
- break;
- case NON_FAT_LITTLE_ENDIAN:
- input_files[i]->cputype=GET32L(header.cputype);
- input_files[i]->cpusubtype=GET32L(header.cpusubtype);
- input_files[i]->narch = 1;
- break;
- case FAT_MAGIC:
- input_files[i]->isfat=1;
- input_files[i]->narch = GET32(((fat_header_t *)&header)->nfat_arch);
- break;
- default:
- /* ignore it */
- break;
- }
- }
- stat_file(i);
- fclose(file);
- }
- output_file.mode = input_files[0]->mode;
- }
-
- /*
- * Find the operation. If we've already found it, then we should quit.
- */
- static operation_t *
- grok_operation(int argc, char *argv[])
- {
- int i;
- const arg_t *found=0;
- const arg_t *current=0;
- int spot=0;
- int numargs;
- operation_t * perform = calloc(sizeof(operation_t),1);
- for(i=1;i<argc;i++){
- if ((current = switch_at(argv,i))){
- switch (current->op){
- case _segalign:
- case _output:
- case _arch:
- /* ignore */
- break;
- default:
- if (found){
- usage();
- } else {
- found=current;
- spot=i;
- }
- }
- }
- }
- if (!found) usage();
- perform->op = found->op;
- numargs = switch_argc(found);
- for(i=0;i<numargs;i++){
- if (spot+i+1>=argc){
- usage();
- }
- perform->argv[i]=argv[spot+i+1];
- }
- perform->argv[i]=0;
- perform->argc=i;
- return perform;
- }
-
- static void
- add_file(char *filename, long type)
- {
- filelist_t * new = calloc(sizeof(filelist_t),1);
- new->filename = filename;
- new->cputype = type;
- input_files[input_file_count++]=new;
- }
-
- /*
- * Input filenames stand alone, or are preceded with -arch
- */
- static void
- grok_infiles(int argc, char *argv[])
- {
- int i;
- const arg_t *arg;
- /* we may need to add one under a -replace operation */
- input_files = calloc(sizeof(filelist_t *),argc+1);
-
- for(i=1;i<argc;i+=switch_argc(arg)+1){
- arg = switch_at(argv,i);
- if (!arg || arg->op == _arch){
- if ((arg && i>=argc-2) || i>=argc) usage();
- add_file(argv[(i)+((arg)?2:0)],cpu_type((arg)?argv[i+1]:0));
- }
- }
- }
-
- static void
- grok_outfile(int argc, char *argv[])
- {
- int i;
- for(i=1;i<argc;i++){
- const arg_t *arg = switch_at(argv,i);
- if (arg && arg->op == _output){
- if (output_file.filename || i>=argc-1) usage();
- output_file.filename=argv[i+1];
- }
- }
- }
-
- static void
- grok_myargs(int argc, char *argv[])
- {
- int i;
- for(i=1;i<argc;i++){
- if (strcmp(argv[i],"-help")==0 ||
- strcmp(argv[i],"--help")==0 ||
- strcmp(argv[i],"-version")==0 ||
- strcmp(argv[i],"--version")==0 ||
- strcmp(argv[i],"-v")==0 ||
- strcmp(argv[i],"-h")==0)
- {
- usage();
- }
- }
- }
-
- /*
- * We want to find a unique operation, build an input filelist
- * and figure out the output file. I could do it efficiently
- * by building a hashtable, etc, but this is OK because the
- * data set is so tiny.
- */
- static operation_t *
- grok_args(int argc, char *argv[])
- {
- operation_t *ret;
- if (argc<=1) {
- usage();
- }
- grok_myargs(argc,argv);
- grok_infiles(argc,argv);
- grok_outfile(argc,argv);
- ret = grok_operation(argc,argv);
- grok_fileinfo();
- return ret;
- }
-
- static fat_arch_t *
- fatgetarch(char *mem, int cpu)
- {
- fat_header_t *fh = (fat_header_t *)mem;
- fat_arch_t *fa;
-
- if (fh && (GET32(fh->magic) == FAT_MAGIC))
- {
- unsigned long num;
- fa = (fat_arch_t *)(fh + 1);
-
- for (num = GET32(fh->nfat_arch); num > 0; num--, fa++)
- {
- if ((cpu == CPU_TYPE_ANY) || (GET32(fa->cputype) == cpu))
- return fa;
- }
- }
- return (fat_arch_t *)0;
- }
-
- static unsigned long
- fatgetoffset(char *mem, int cpu)
- {
- fat_arch_t *fa, *fa0;
-
- if ((fa0 = fatgetarch(mem, CPU_TYPE_ANY)) && (fa = fatgetarch(mem, cpu)))
- return GET32(fa->offset);
- else
- return 0;
- }
-
- static unsigned long
- fatgetsize(char *mem, int cpu)
- {
- fat_arch_t *fa;
-
- if ((fa = fatgetarch(mem, cpu)))
- return GET32(fa->size);
- else
- return 0;
- }
-
- static void
- fatsave(char *file, char *mem, int cpu)
- {
- FILE *out;
- fat_arch_t *fa, *fa0;
-
- if (file && ((out = fopen(file, "w")) != NULL))
- {
- if ((fa0 = fatgetarch(mem, CPU_TYPE_ANY)) && (fa = fatgetarch(mem, cpu)))
- {
- char num[4], msg[] = "\nExtracted with qlipo\n";
- unsigned long t;
-
- fwrite(mem, 4, 1, out);
- SET32(num, 1);
- fwrite(num, sizeof(num), 1, out);
- t = GET32(fa->offset);
- COPY32(fa->offset, fa0->offset);
- fwrite(fa, sizeof(fat_arch_t), (size_t)1, out);
- SET32(fa->offset, t);
- fwrite(msg, sizeof(msg), 1, out);
- fwrite(mem, (size_t)(GET32(fa0->offset) - sizeof(msg) -
- sizeof(fat_arch_t) - 4 - 4), 1, out);
- fwrite(mem + fatgetoffset(mem, cpu),
- (size_t)fatgetsize(mem, cpu), 1, out);
- }
-
- fclose(out);
- }
- }
-
- static char *
- fatload(char *filename)
- {
- FILE *file = stdin;
- char *mem = NULL;
- long old, size = 16384;
-
- if (filename && (!strcmp(filename, "-") || (file = fopen(filename, "r"))))
- {
- for (mem = (char *)malloc((size_t)size), old = 0;
- mem = (char *)realloc(mem, (size_t)size);
- old += (size - old), size *= 2)
- {
- if (fread(mem + old, 1, (size_t)(size - old), file) < (size - old))
- {
- if (ferror(file))
- {
- free(mem);
- mem = NULL;
- }
- break;
- }
- }
- fclose(file);
- }
- return mem;
- }
-
- static long
- type_from_header(non_fat_header_t *nfh)
- {
- long ct = CPU_TYPE_ANY;
- if (GET32(nfh->magic) == NON_FAT_BIG_ENDIAN)
- {
- ct = (int)GET32(nfh->cputype);
- } else if (GET32(nfh->magic) == NON_FAT_LITTLE_ENDIAN)
- {
- ct = (int)GET32L(nfh->cputype);
- }
- return ct;
- }
-
- static long
- type_from_fat_arch(fat_arch_t *fa)
- {
- return GET32(fa->cputype);
- }
-
- static long
- byte_alignment(fat_arch_t *fa)
- {
- return pow(2,GET32(fa->align));
- }
-
- static void
- fatanalyse(char *mem, const char *filename)
- {
- fat_header_t *fh = (fat_header_t *)mem;
- non_fat_header_t *nfh = (non_fat_header_t *)mem;
- int ct = -1;
-
- if (fh)
- {
- if (GET32(fh->magic) == FAT_MAGIC)
- {
- long num = GET32(fh->nfat_arch);
- fat_arch_t *fa = (fat_arch_t *)(fh + 1);
-
- printf("Architectures (%ld) in the fat file: \"%s\" are:", num, filename);
- for ( ; num > 0; fa++, num--)
- {
- int ct = (int)GET32(fa->cputype);
-
- if ((ct < -1) || (ct >= (sizeof(cpus) / sizeof(char *))))
- ct = 1;
- printf(" %s", cpus[ct + 1]);
- }
- printf("\n");
- } else {
- ct = type_from_header(nfh);
- if (ct >= 0) {
- printf("Non-fat file: \"%s\" is architecture (%d)", filename,ct);
- if ((ct < -1) || (ct >= (sizeof(cpus) / sizeof(char *))))
- ct = 1;
- printf(": %s\n", cpus[ct + 1]);
- } else {
- printf("unknown file type (magic: %08x)\n", (unsigned)GET32(fh->magic));
- }
- }
- }
- }
-
- static void
- info(void)
- {
- char * mem;
- int i;
- for(i=0;input_files[i];i++){
- if ((mem = fatload(input_files[i]->filename)))
- {
- fatanalyse(mem,input_files[i]->filename);
- free(mem);
- }
- }
- }
-
- static void
- fatprint_header(fat_header_t *header, const char *filename)
- {
- if (IS_FAT(header)){
- printf("%s: Fat header in: \"%s\"\n"
- "fat_magic 0x%lx\n"
- "nfat_arch %lx\n",
- PROGRAM_NAME,
- filename,
- GET32(header->magic),
- GET32(header->nfat_arch));
- } else {
- printf("Input file \"%s\" is not a fat file.\n",filename);
- }
- }
-
- static void
- fatprint_arch(fat_arch_t *arch)
- {
- long type = GET32(arch->cputype);
- long subtype = GET32(arch->cpusubtype);
- const char * architecture = arch_from_type(type);
- const char * cputype = get_cputype(type);
- const char * cpusubtype = get_cpusubtype(type,subtype);
- long offset = GET32(arch->offset);
- long size = GET32(arch->size);
- long align = GET32(arch->align);
- printf("architecture %s\n"
- " cputype %s\n"
- " cpusubtype %s\n"
- " offset %ld\n"
- " size %ld\n"
- " align 2^%ld (%.0f)\n",
- architecture,cputype,cpusubtype,offset,size,align,pow(2.0,(float)align));
- }
-
- static void
- fatprint(const char *filename)
- {
- FILE *file = fopen(filename,"r");
- fat_header_t header;
- fat_arch_t arch;
- if (!file) {
- fprintf(stderr,"%s: Can't open \"%s\", (%s)\n",
- PROGRAM_NAME,
- filename,strerror(errno));
- quit(1);
- }
- fread(&header,1,HEADER_BYTE_COUNT,file);
- fatprint_header(&header,filename);
- if (IS_FAT(&header)){
- long i,count=GET32(header.nfat_arch);
- for(i=0;i<count;i++){
- fread(&arch,1,ARCH_BYTE_COUNT,file);
- fatprint_arch(&arch);
- }
- } else {
- /* nfat_arch is filled with the cpu type if the file is not fat */
- long type = type_from_header((non_fat_header_t *)&header);
- printf("Non-fat file: \"%s\" is architecture: %s\n",
- filename,
- arch_from_type(type));
- }
- fclose(file);
- }
-
-
- static void
- detailed_info(void)
- {
- int i;
- for(i=0;input_files[i];i++){
- fatprint(input_files[i]->filename);
- }
- }
-
- static void
- write_fat_header(FILE *file, long archs)
- {
- fat_header_t header;
- SET32(header.magic,FAT_MAGIC);
- SET32(header.nfat_arch,archs);
- if (fwrite(&header,1,HEADER_BYTE_COUNT,file)!=HEADER_BYTE_COUNT){
- fprintf(stderr,"Error writing to file... (%s)\n",strerror(errno));
- quit(1);
- }
- }
-
- static void
- set_fat_arch(fat_arch_t *a,long ct, long cst, long o, long s, long algn)
- {
- SET32(a->cputype,ct);
- SET32(a->cpusubtype,cst);
- SET32(a->offset,o);
- SET32(a->size,s);
- SET32(a->align,algn);
- }
-
- static fat_arch_t *
- make_fat_arch_at(long which)
- {
- long size = input_files[which]->size;
- long offset = 0; /* we consider it pure data... */
- long cputype = input_files[which]->cputype;
- long cpusubtype = input_files[which]->cpusubtype;
- long align = DEFAULT_ALIGN;
- fat_arch_t * a;
- if (cputype == CPU_TYPE_ANY){
- fprintf(stderr,"%s: Can't figure out the arch type for \"%s\".\n",
- PROGRAM_NAME,input_files[which]->filename);
- usage();
- }
- a = calloc(sizeof(fat_arch_t),1);
- set_fat_arch(a,cputype,cpusubtype,offset,size,align);
- return a;
- }
-
-
- static long
- add_arch(fat_arch_t ***fa, fat_arch_t *add, long at)
- {
- if (!*fa) {
- *fa = calloc(sizeof(fat_arch_t *),1);
- } else {
- *fa = realloc(*fa, sizeof(fat_arch_t *)*(at+1));
- }
- (*fa)[at]=add;
- return 1;
- }
-
- static long
- load_fat_arch(long which, fat_arch_t ***fa, long total)
- {
- FILE * file = fopen(input_files[which]->filename,"r");
- fat_header_t header;
- fat_arch_t *a;
- if (!file) { /* Can't assume it's still here. */
- fprintf(stderr,"%s: Can't open \"%s\", (%s)\n",
- PROGRAM_NAME,
- input_files[which]->filename,strerror(errno));
- quit(1);
- }
- fread(&header,1,HEADER_BYTE_COUNT,file);
- if (input_files[which]->isfat){
- long i,count=GET32(header.nfat_arch);
- for(i=0;i<count;i++){
- a = calloc(sizeof(fat_arch_t),1);
- fread(a,1,ARCH_BYTE_COUNT,file);
- total += add_arch(fa,a,total);
- }
- } else {
- a = make_fat_arch_at(which);
- total += add_arch(fa,a,total);
- }
- fclose(file);
- return total;
- }
-
- static fat_arch_t **
- load_fat_archs(long *total)
- {
- long i;
- long count = 0;
- fat_arch_t **fa = 0;
- for(i=0;i<input_file_count;i++){
- input_files[i]->start = count;
- count = load_fat_arch(i,&fa,count);
- }
- *total = count;
- return fa;
- }
-
- static long
- file_owning_fa(long chunk)
- {
- int i;
- for(i=input_file_count-1;i>0;i--){
- if (input_files[i]->start <= chunk){
- return i;
- }
- }
- return 0;
- }
-
- static void
- consistency_check(fat_arch_t **fa, long count)
- {
- long archs[CPU_TYPE_MAX];
- long i;
- for(i=0;i<CPU_TYPE_MAX;i++) archs[i]=-1;
- for(i=0;i<count;i++){
- long current = type_from_fat_arch(fa[i]);
- long which, suck, skip;
- if (current<0 || current>=CPU_TYPE_MAX){
- fprintf(stderr,"%s: File \"%s\" has unknown architecture.\n",
- PROGRAM_NAME,input_files[file_owning_fa(i)]->filename);
- quit(1);
- }
- which = file_owning_fa(i);
- suck = input_files[which]->suck;
- skip = input_files[which]->skip;
- /*
- * See if this file is losing this architecture, or if it is the
- * only one being preserved.
- */
- if (skip != current && (!suck || suck==current)){
- if (archs[current]!=-1) {
- fprintf(stderr,
- "%s: \"%s\" and \"%s\" share common architecture (%s)\n"
- " and can't exist in the same fat output file.\n",
- PROGRAM_NAME,
- input_files[archs[current]]->filename,
- input_files[file_owning_fa(i)]->filename,
- get_cputype(current));
- quit(1);
- }
- archs[current] = which;
- }
- }
- }
-
- static int
- files_are_identical(const char *f1, const char *f2)
- {
- if (strcmp(f1,f2)==0){
- return 1;
- } else {
- struct stat sb1, sb2;
- if (stat(f1,&sb1)){
- fprintf(stderr,"%s: Error statting \"%s\", (%s)\n",
- PROGRAM_NAME, f1,strerror(errno));
- quit(1);
- }
- if (stat(f2,&sb2)){
- fprintf(stderr,"%s: Error statting \"%s\", (%s)\n",
- PROGRAM_NAME, f1,strerror(errno));
- quit(1);
- }
- return (sb1.st_ino == sb2.st_ino);
- }
- }
-
- static FILE *
- muddle_outfile(void)
- {
- int i;
- FILE * file = 0;
- for(i=0;i<input_file_count;i++){
- if (files_are_identical(output_file.filename,input_files[i]->filename)){
- overwrite_file = malloc(MAXPATHLEN);
- sprintf(overwrite_file,"/tmp/%s.%d",PROGRAM_NAME,getpid());
- file = (fopen(overwrite_file,"w"));
- }
- }
- if (!file) {
- file = fopen(output_file.filename,"w");
- }
- if (!(file)){
- fprintf(stderr,"%s: Error opening \"%s\", (%s)\n",
- PROGRAM_NAME, overwrite_file ? overwrite_file :
- output_file.filename,strerror(errno));
- quit(1);
- }
- return file;
- }
-
- static void
- copy_filename(const char *dest, const char *source)
- {
- char tmp[strlen(dest)+strlen(source)+128];
- sprintf(tmp,"cp %s %s",source,dest);
- system(tmp);
- }
-
- static void
- close_output(FILE *file)
- {
- FILE * source, *output;
- fclose(file);
- if (overwrite_file){
- output = fopen(output_file.filename,"w");
- if (!(output)){
- fprintf(stderr,"%s: Error opening \"%s\", (%s)\n",
- PROGRAM_NAME, output_file.filename,strerror(errno));
- quit(1);
- }
- source = fopen(overwrite_file,"r");
- if (!(source)){
- fprintf(stderr,"%s: Error opening \"%s\", (%s)\n",
- PROGRAM_NAME, overwrite_file,strerror(errno));
- quit(1);
- }
- fclose(source);
- fclose(output);
- copy_filename(output_file.filename,overwrite_file);
- }
- chmod(output_file.filename,output_file.mode);
- }
-
- static FILE *
- grok_output(void)
- {
- FILE * file;
- if (!output_file.filename)
- {
- fprintf(stderr,"%s: No output file specified.\n",PROGRAM_NAME);
- usage();
- }
- file = muddle_outfile();
- return file;
- }
-
-
- static void
- write_fat_archlist(FILE *output, fat_arch_t **fa, long total)
- {
- long offset=0;
- long align,j,i,stop;
- long old_offset;
- long skip;
- long suck;
- for(i=0;i<input_file_count;i++){
- if (i<input_file_count-1){
- stop = input_files[i+1]->start;
- } else {
- stop = total;
- }
- skip = input_files[i]->skip;
- suck = input_files[i]->suck;
- for(j=input_files[i]->start;j<stop;j++){
- long current = GET32(fa[j]->cputype);
- if (current != skip){
- if (current == suck || !suck){
- align = byte_alignment(fa[j]);
- offset = ((offset / align) + 1) * align;
- old_offset = GET32(fa[j]->offset);
- SET32(fa[j]->offset,offset);
- fwrite(fa[j],1,ARCH_BYTE_COUNT,output);
- offset+=GET32(fa[j]->size);
- /* we set it back so we know were to get it later */
- SET32(fa[j]->offset,old_offset);
- }
- }
- }
- }
- }
-
- static void
- write_pad(FILE *output, long start, long align)
- {
- long count = (align) ? align - start % align : 0;
- long i;
- char null='\0';
- for(i=0;i<count;i++){
- fwrite(&null,1,1,output);
- }
- }
-
- static void
- write_binary(FILE *output, long align, long which, long where, long size)
- {
- long tell = ftell(output);
- /* assume it's still ok */
- FILE * source = fopen(input_files[which]->filename,"r");
- long bufsiz;
- long current;
- write_pad(output,tell,align);
- fseek(source,where,SEEK_SET);
- bufsiz = (BUFFER_SIZE > input_files[which]->size) ?
- input_files[which]->size : BUFFER_SIZE;
-
- for(current=bufsiz;(size>0);){
- if (fread(buffer,1,current,source)!=current){
- fprintf(stderr,"%s: Error reading from \"%s\", (%s)\n",
- PROGRAM_NAME,input_files[which]->filename,strerror(errno));
- }
- if (fwrite(buffer,1,current,output)!=current){
- fprintf(stderr,"%s: Error writing to \"%s\", (%s)\n",
- PROGRAM_NAME,output_file.filename,strerror(errno));
- }
- size -= current;
- if (size<bufsiz){
- current = size;
- }
- }
- fclose(source);
- }
-
- static void
- write_fat_binaries(FILE *output, fat_arch_t **fa, long total)
- {
- long offset=0;
- long align,j,i,stop;
- long where,size;
- long skip, suck;
- for(i=0;i<input_file_count;i++){
- if (i<input_file_count-1){
- stop = input_files[i+1]->start;
- } else {
- stop = total;
- }
- skip = input_files[i]->skip;
- suck = input_files[i]->suck;
- for(j=input_files[i]->start;j<stop;j++){
- long current = GET32(fa[j]->cputype);
- if (current != skip){
- if (current == suck || !suck){
- align = byte_alignment(fa[j]);
- offset = ((offset / align) + 1) * align;
- where = GET32(fa[j]->offset);
- size = GET32(fa[j]->size);
- write_binary(output,align,i,where,size);
- offset+=size;
- }
- }
- }
- }
- }
-
- /* take the infiles, and meld them into the outfile */
- static void
- create(const operation_t *perform)
- {
- FILE * output;
- long total;
- fat_arch_t **fa;
- fa = load_fat_archs(&total);
- consistency_check(fa,total);
- output = grok_output();
- write_fat_header(output,total);
- write_fat_archlist(output,fa,total);
- write_fat_binaries(output,fa,total);
- close_output(output);
- }
-
- static fat_arch_t *
- find_fat_arch(fat_arch_t **fa, long count, int type)
- {
- long i;
- for(i=0;i<count;i++){
- if (GET32(fa[i]->cputype)==type){
- return fa[i];
- }
- }
- return 0;
- }
-
- static void
- assert_one_input(const char *tag)
- {
- if (input_file_count!=1){
- fprintf(stderr,"%s: %s requires that you specify a single "
- "input file.\n", PROGRAM_NAME, tag);
- usage();
- }
- }
-
- static void
- assert_arch_arg(const operation_t *perform, const char *tag)
- {
- if (perform->argc<1 || cpu_type(perform->argv[0])<=CPU_TYPE_ANY){
- fprintf(stderr, "%s: %s requires an architecture argument, "
- "like \"m68k\"\n", PROGRAM_NAME,tag);
- usage();
- }
- }
-
- static void
- assert_fat(int which, const char *tag)
- {
- if (!input_files[which]->isfat){
- fprintf(stderr,"%s: \"%s\" is not fat, and %s only operates "
- "on fat files.\n",PROGRAM_NAME,input_files[which]->filename,tag);
- quit(1);
- }
- }
-
- static void
- assert_arch(fat_arch_t *single, long type, const char *tag)
- {
- if (!single){
- fprintf(stderr,
- "%s: \"%s\" doesn't contain the architecture \"%s\" passed "
- "to %s.\n",
- PROGRAM_NAME,input_files[0]->filename,get_cputype(type),tag);
- quit(1);
- }
- }
-
- /*
- * Take the infiles, and meld them into a fat outfile with one arch
- */
- static void
- extract(const operation_t *perform)
- {
- int type;
- FILE * output;
- long total;
- fat_arch_t **fa;
- fat_arch_t *single;
-
- assert_arch_arg(perform,"-extract");
- type = cpu_type(perform->argv[0]);
- assert_one_input("-extract");
- fa = load_fat_archs(&total);
- assert_fat(0,"-extract");
- consistency_check(fa,total);
- single = find_fat_arch(fa,total,type);
- assert_arch(single,type,"-extract");
- output = grok_output();
- write_fat_header(output,1);
- write_fat_archlist(output,&single,1);
- write_fat_binaries(output,&single,1);
- close_output(output);
- }
-
- /*
- * Take the infile, and meld it into a thin outfile
- */
- static void
- thin(const operation_t *perform)
- {
- FILE * output;
- long total;
- fat_arch_t **fa, *single;
- int type;
-
- assert_arch_arg(perform,"-thin");
- type = cpu_type(perform->argv[0]);
- assert_one_input("-thin");
- fa = load_fat_archs(&total);
- assert_fat(0,"-thin");
- consistency_check(fa,total);
- single = find_fat_arch(fa,total,type);
- assert_arch(single,type,"-thin");
- output = grok_output();
- write_binary(output,0,0,GET32(single->offset),GET32(single->size));
- close_output(output);
- }
-
- static void
- assert_file_arg(const operation_t *perform, int index, const char *tag)
- {
- if (perform->argc<=index){
- fprintf(stderr,"%s: %s requires more arguments than was given (%d)\n",
- PROGRAM_NAME,tag,index);
- usage();
- }
- }
-
- /*
- * Take the infile, and replace the named arch with the named arch
- * from the argument file.
- */
- static void
- replace(const operation_t *perform)
- {
- FILE * output;
- long total;
- fat_arch_t **fa, *single;
- int type;
-
- assert_one_input("-replace");
- assert_fat(0,"-replace");
- assert_arch_arg(perform,"-replace");
- type = cpu_type(perform->argv[0]);
- assert_file_arg(perform,1,"-replace");
- add_file(perform->argv[1],CPU_TYPE_ANY);
- /*
- * Rerun this to assert the new file we just added.
- */
- grok_fileinfo();
- /*
- * file 0 is the source file, file 1 is the suck file
- */
- input_files[0]->skip = type;
- input_files[1]->suck = type;
- fa = load_fat_archs(&total);
- consistency_check(fa,total);
- single = find_fat_arch(fa,total,type);
- /* this is wrong... we have to check both files */
- assert_arch(single,type,"-replace");
- output = grok_output();
- write_fat_header(output,input_files[0]->narch);
- write_fat_archlist(output,fa,total);
- write_fat_binaries(output,fa,total);
- close_output(output);
- }
-
- static void
- assert_total(int archcount, const char *tag)
- {
- if (!archcount){
- fprintf(stderr, "%s: %s operation would result in an empty fat file\n",
- PROGRAM_NAME,tag);
- quit(1);
- }
- }
-
- /*
- * Remove the named architecture.
- */
- static void
- remove_arch(const operation_t *perform)
- {
- FILE * output;
- long total;
- fat_arch_t **fa, *single;
- int type;
-
- assert_arch_arg(perform,"-remove");
- type = cpu_type(perform->argv[0]);
-
- assert_one_input("-remove");
- fa = load_fat_archs(&total);
- assert_fat(0,"-remove");
- consistency_check(fa,total);
- single = find_fat_arch(fa,total,type);
- assert_arch(single,type,"-remove");
- output = grok_output();
- assert_total(total-1,"-remove");
- write_fat_header(output,total-1);
- input_files[0]->skip = type;
- write_fat_archlist(output,fa,total);
- write_fat_binaries(output,fa,total);
- close_output(output);
- }
-
- void
- handler(int sig)
- {
- const char *die=0;
- switch(sig)
- {
- case SIGINT:
- die = PROGRAM_NAME ": Caught an INTERRUPT. Bye.\n";
- break;
- case SIGHUP:
- die = PROGRAM_NAME ": Caught a HANGUP. That's rude! Bye.\n";
- break;
- case SIGTERM:
- die = PROGRAM_NAME ": Caught a SIGTERM. Bye.\n";
- break;
- }
- if (die){
- fprintf(stderr,"%s",die);
- quit(1);
- }
- }
-
- static void
- setup_signals(void)
- {
- signal(SIGINT,handler);
- signal(SIGHUP,handler);
- signal(SIGTERM,handler);
- }
-
- int
- main(int argc, char **argv)
- {
- int i, xcpu = -1;
- char *mem;
- extern int optind;
- operation_t * perform;
- #ifdef DEBUG
- ArgC = argc;
- ArgV = argv;
- #endif
- setup_signals();
- perform = grok_args(argc,argv);
- switch(perform->op){
- case _info:
- info();
- break;
- case _detailed_info:
- detailed_info();
- break;
- case _create:
- create(perform);
- break;
- case _extract:
- extract(perform);
- break;
- case _thin:
- thin(perform);
- break;
- case _replace:
- replace(perform);
- break;
- case _remove:
- remove_arch(perform);
- break;
- default: /* arch, output, segalign */
- usage();
- break;
- }
-
- quit(0);
-
-
- if (xcpu > 0)
- printf("Extracting cpu type '%s'\n", cpus[xcpu + 1]);
-
- if (optind == argc)
- {
- }
-
- for (i = optind; i < argc; i++)
- {
- if ((mem = fatload(argv[i])))
- {
- char buf[1024];
-
- printf("File '%s': ", argv[i]);
- fatanalyse(mem,0);
-
- if (xcpu > 0)
- {
- if (fatgetarch(mem, xcpu))
- {
- sprintf(buf, "%s.%s", argv[i], cpus[xcpu + 1]);
- fatsave(buf, mem, xcpu);
- }
- else
- printf("specified architecture not found.\n");
- }
-
- free(mem);
- }
- }
-
- return 0;
- }
-